package org.saxonextensions.eml.dispatcher.jboss;

import org.jboss.system.ServiceMBeanSupport;

import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.naming.*;
import javax.mail.Message;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.InternetAddress;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;

/**
 * EML Dispatcher Service bean, bind EMLDispatcher impl to {@link #JNDI_NAME}.
 */
public class EMLDispatcherService extends ServiceMBeanSupport implements EMLDispatcherServiceMBean {

  /** Default JNDI Name */
  public static final String JNDI_NAME = "java:/EMLDispatcher";
  /** JNDI name to bind dispatcher to */
  private String jndiName = JNDI_NAME;
  /** JNDI name of the default JBoss Mail service */
  private String mailServiceJndiName = "java:/Mail";
  /** Dispatcher instance */
  protected ThreadedEMLDispatcherImpl dispatcher;

  /**
   * The JNDI name to bind this service to
   *
   * @param name JNDI name to bind this service to
   */
  public void setJndiName(String name) {
    this.jndiName = name;
  }

  /**
   * Return JNDI name this service is bound to
   *
   * @return JNDI name this service is bound to
   */
  public String getJndiName() {
    return jndiName;
  }

  /**
   * Return mail service JNDI name
   *
   * @return JBoss mail service JNDI name
   */
  public String getMailServiceJndiName() {
    return mailServiceJndiName;
  }

  /**
   * Set mail service JNDI name
   *
   * @param mailServiceJndiName JBoss mail service JNDI name
   */
  public void setMailServiceJndiName(String mailServiceJndiName) {
    this.mailServiceJndiName = mailServiceJndiName;
  }

  /**
   * Return dispatcher max threads
   *
   * @return max threads
   */
  public int getMaxThreads() {
    return dispatcher.getMaxThreads();
  }

  /**
   * Set dispatcher max threds
   *
   * @param maxThreads max thread count
   */
  public void setMaxThreads(int maxThreads) {
    dispatcher.setMaxThreads(maxThreads);
  }

  /**
   * Return number of currently busy threads
   *
   * @return number of busy threads
   */
  public int getBusyThreads() {
    return dispatcher.getCounter();
  }

  /**
   * Return Object name
   *
   * @param server server
   * @param name name
   * @return this object name
   * @throws MalformedObjectNameException never thrown
   */
  protected ObjectName getObjectName(MBeanServer server, ObjectName name) throws MalformedObjectNameException {
    return name == null ? OBJECT_NAME : name;
  }

  /**
   * Start service
   *
   * @throws Exception when unable to start the service
   */
  protected void startService() throws Exception {
    log.info("Starting service "+getJndiName());
    // createa new dispatcher
    dispatcher = new ThreadedEMLDispatcherImpl();
    // set email service jndi name
    dispatcher.setMailServiceJndiName(getMailServiceJndiName());
    // bind it to JNDI
    bind();
  }

  /**
   * Stop service
   *
   * @throws Exception when unable to stop the service
   */
  protected void stopService() throws Exception {
    log.info("Stopping service "+getJndiName());
    // unbind service
    unbind();
    // stop dispatcher
    dispatcher.stop();
    // call super
    super.stop();
  }

  /**
   * Destroy service
   */
  public void destroyService() throws Exception{
    // unbind service
    unbind();
    // stop dispatcher
    dispatcher.stop();
    // destroy dispatcher
    dispatcher.destroy();
  }

  /**
   * Send a test eMail to given email address
   *
   * @param mailto email address
   */
  public void sendTestEmail(String mailto) throws Exception {
    Message message = new MimeMessage(dispatcher.getSession());
    message.setSubject(jndiName+" service test message.");
    message.setFrom(new InternetAddress(mailto));
    message.setRecipient(Message.RecipientType.TO, new InternetAddress(mailto));    
    message.setText("This is the test message, please do not responde.");
    // serialize message
    ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
    message.writeTo(out);
    // dispatch the message
    dispatcher.dispatch(new ByteArrayInputStream(out.toByteArray()));
  }

  /**
   * Return debug flag
   *
   * @return debug flag
   */
  public boolean getDebug() {
    return dispatcher.getDebug();
  }

  /**
   * Set debug flag
   *
   * @param debug debug flag
   */
  public void setDebug(boolean debug) {
    dispatcher.setDebug(debug);
  }

  /**
   * Bind the service
   *
   * @throws NamingException when unable to bind the service
   */
  protected void bind() throws NamingException {
    Context ctx = new InitialContext();
    try {
      Name n = ctx.getNameParser("").parse(jndiName);
      while (n.size() > 1) {
        String ctxName = n.get(0);
        try {
          ctx = (Context) ctx.lookup(ctxName);
        }
        catch (NameNotFoundException e) {
          ctx = ctx.createSubcontext(ctxName);
        }
        n = n.getSuffix(1);
      }

      ctx.bind(n.get(0), dispatcher);
    }
    finally {
      ctx.close();
    }

    log.info("Service is bound to " + jndiName);
  }

  /**
   * Unbind service
   *
   * @throws NamingException when unable to unbind the service
   */
  protected void unbind() throws NamingException {
    if (jndiName != null) {
      InitialContext ctx = new InitialContext();
      try {
        ctx.unbind(jndiName);
        log.info("Service " + jndiName + " is unbound.");
      } catch (NameNotFoundException ex) {
        // ignore NameNotFoundException 
        log.debug(ex.getMessage(), ex);
      } finally {
        ctx.close();
      }
    }
  }

}
